#import "RNStepperCell.h"
#import "NSBezierPath+MCAdditions.h"

//static NSColor *RNStepperArrowColor;
//static NSColor *RNStepperArrowPressedColor;

#pragma mark -
@interface RNStepperArrowButtonCell : NSButtonCell {
    BOOL facesLeft_;
    NSTextFieldCell *parentCell_;
    NSColor *arrowColor_;
    NSColor *pressedColor_;
}
@property (assign) BOOL facesLeft;
@property (assign) NSTextFieldCell *parentCell;
@property (retain) NSColor *arrowColor;
@property (retain) NSColor *pressedColor;

-(id)initWithArrowDirection:(BOOL)facesLeft;
@end

@implementation RNStepperArrowButtonCell
@synthesize facesLeft = facesLeft_;
@synthesize parentCell = parentCell_;
@synthesize arrowColor = arrowColor_;
@synthesize pressedColor = pressedColor_;

//+(void)initialize {
//    RNStepperArrowColor = [NSColor colorWithCalibratedWhite:0.41f alpha:1.0f];
//    RNStepperArrowPressedColor = [NSColor colorWithCalibratedWhite:0.21f alpha:1.0f];
//}

-(id)initWithArrowDirection:(BOOL)facesLeft {
    if (!(self = [super initImageCell:nil]))
        return nil;
    [self setFacesLeft:facesLeft];
    [self setArrowColor:[NSColor colorWithCalibratedWhite:0.41f alpha:1.0f]];
    [self setPressedColor:[NSColor colorWithCalibratedWhite:0.21f alpha:1.0f]];
    return self;
}

NSString * const RNStepperArrowButtonCellFacesLeftKey = @"facesLeft";
NSString * const RNStepperArrowColorKey = @"RNStepperArrowColorKey";
NSString * const RNStepperPressedArrowColorKey = @"RNStepperPressedArrowColorKey";

-(id)initWithCoder:(NSCoder *)coder {
    if (!(self = [super initWithCoder:coder]))
        return nil;
    [self setFacesLeft:[coder decodeBoolForKey:RNStepperArrowButtonCellFacesLeftKey]];
    [self setArrowColor:[coder decodeObjectForKey:RNStepperArrowColorKey]];
    [self setPressedColor:[coder decodeObjectForKey:RNStepperPressedArrowColorKey]];
    return self;
}

-(void)encodeWithCoder:(NSCoder *)coder {
    [super encodeWithCoder:coder];
    [coder encodeBool:[self facesLeft] forKey:RNStepperArrowButtonCellFacesLeftKey];
    [coder encodeObject:[self arrowColor] forKey:RNStepperArrowColorKey];
    [coder encodeObject:[self pressedColor] forKey:RNStepperPressedArrowColorKey];
}

-(id)copyWithZone:(NSZone *)zone {
    RNStepperArrowButtonCell *cellCopy = [super copyWithZone:zone];
    [cellCopy setFacesLeft:[self facesLeft]];
    [cellCopy setArrowColor:[self arrowColor]];
    [cellCopy setPressedColor:[self pressedColor]];
    return cellCopy;
}

-(void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView { 
    NSRect arrowFrame = cellFrame;
    CGFloat oneHalfSlice = floor(NSWidth(cellFrame) / 2);
    CGFloat oneQuarterSlice = floor(oneHalfSlice / 2);
    arrowFrame.origin.x += oneQuarterSlice;
    arrowFrame.origin.y += floor((NSHeight(cellFrame) - oneHalfSlice) / 2);
    arrowFrame.size.width = oneHalfSlice;
    arrowFrame.size.height = oneHalfSlice;
    
    // Create the arrow path centered around the origin, facing right
    NSBezierPath *arrowPath = [NSBezierPath bezierPath];
    [arrowPath moveToPoint:NSMakePoint(-(NSWidth(arrowFrame) / 2), -(NSHeight(arrowFrame) / 2))];
    [arrowPath lineToPoint:NSMakePoint((NSWidth(arrowFrame) / 2), 0.0f)];
    [arrowPath lineToPoint:NSMakePoint(-(NSWidth(arrowFrame) / 2), (NSHeight(arrowFrame) / 2))];
    [arrowPath closePath];
    
    // If this is the left arrow, rotate it
    NSAffineTransform *rotateTransform = [NSAffineTransform transform];
    if ([self facesLeft]) 
        [rotateTransform rotateByDegrees:180.0f];
    
    // Translate the path into the cell frame
    NSAffineTransform *translateTransform = [NSAffineTransform transform];
    [translateTransform translateXBy:(NSWidth(arrowFrame) / 2) yBy:(NSHeight(arrowFrame) / 2)];
    [translateTransform translateXBy:arrowFrame.origin.x yBy:arrowFrame.origin.y];
    
    // Apply transforms to the path
    [rotateTransform appendTransform:translateTransform];
    [arrowPath transformUsingAffineTransform:rotateTransform];
    
    NSColor *arrowColor = [self isHighlighted] ? [self pressedColor] : [self arrowColor];
    [arrowColor setFill];
    [arrowPath fill];
}

- (BOOL)trackMouse:(NSEvent *)theEvent inRect:(NSRect)cellFrame ofView:(NSView *)controlView untilMouseUp:(BOOL)untilMouseUp {
    NSInteger currentValue = [[self parentCell] integerValue]; // Cache value since this will reset as cells are drawn inside tracking loop

    while ([theEvent type] != NSLeftMouseUp) {
        NSPoint eventPoint = [controlView convertPoint:[theEvent locationInWindow] fromView:nil];
        BOOL mouseInButton = [controlView mouse:eventPoint inRect:cellFrame];
        if (mouseInButton && ![self isHighlighted]) {
            [self setHighlighted:YES];
            [controlView setNeedsDisplayInRect:cellFrame];
        }
        if (!mouseInButton && [self isHighlighted]) {
            [self setHighlighted:NO];
            [controlView setNeedsDisplayInRect:cellFrame];
        }
        theEvent = [[controlView window] nextEventMatchingMask:(NSLeftMouseUpMask | NSLeftMouseDraggedMask)];
    }

    NSPoint finalPoint = [controlView convertPoint:[theEvent locationInWindow] fromView:nil];
    if ([controlView mouse:finalPoint inRect:cellFrame]) {
        [self setHighlighted:NO];
        [controlView setNeedsDisplayInRect:cellFrame];
        NSInteger newValue = currentValue + ([self facesLeft] ? -1 : 1);
        if (newValue > 0 && newValue < 100) {
            [[self parentCell] setIntegerValue:newValue];
            NSDictionary *info = [(NSControl *)[self controlView] infoForBinding:@"value"];
            if (info) {
                id object = [info valueForKey:NSObservedObjectKey];
                NSString *keyPath = [info valueForKey:NSObservedKeyPathKey];
                [object setValue:[NSNumber numberWithInteger:newValue] forKeyPath:keyPath];
            }
        }
    }
    return YES;
}

@end


#pragma mark -
@interface RNStepperCell (RNStepperCellPrivateMethods)
- (void)drawBezelWithFrame_:(NSRect)cellFrame inView:(NSView *)controlView;
-(void)initializeStepperCell_;
-(void)initializeArrowCells_;
-(void)initializeFormatter_;
@end

static NSGradient *stepperCellBackgroundGradient;
static NSColor *stepperCellBorderColor;
static NSShadow *stepperCellInnerShadow;

#pragma mark -
@implementation RNStepperCell

@synthesize leftArrowCell = leftArrowCell_;
@synthesize rightArrowCell = rightArrowCell_;
@synthesize arrowLength = arrowLength_;

+(void)initialize {
    stepperCellBackgroundGradient = [[NSGradient alloc] initWithStartingColor:[NSColor colorWithCalibratedWhite:0.95f alpha:1.0f] endingColor:[NSColor colorWithCalibratedWhite:1.0f alpha:1.0f]];
    stepperCellBorderColor = [NSColor colorWithCalibratedWhite:0.87f alpha:1.0f];
    stepperCellInnerShadow = [[NSShadow alloc] init];
    [stepperCellInnerShadow setShadowColor:[NSColor colorWithCalibratedWhite:0.0f alpha:0.5f]];
    [stepperCellInnerShadow setShadowBlurRadius:2.0f];
}

-(id)initTextCell:(NSString *)title {
    if (!(self = [super initTextCell:title]))
        return nil;
    
    [self initializeStepperCell_];    
    return self;
}

-(id)initImageCell:(NSImage *)image {
    [NSException raise:NSInternalInconsistencyException format:@"Initializing RNStepperCell as image-type cell is not valid"];
    return nil;
}

-(id)initWithCoder:(NSCoder *)coder {
    if (!(self = [super initWithCoder:coder]))
        return nil;
    
    [self initializeStepperCell_];
    return self;
}

-(void)awakeFromNib {
    [self initializeStepperCell_];
}

-(void)initializeStepperCell_ {
    [self setAlignment:NSCenterTextAlignment];
    [self initializeArrowCells_];
    [self initializeFormatter_];    
}

-(void)initializeFormatter_ {
    NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
    [formatter setAllowsFloats:NO];
    [formatter setMinimum:[NSNumber numberWithInteger:1]];
    [formatter setMaximumIntegerDigits:2];
    [self setFormatter:formatter];
}

-(void)initializeArrowCells_ {
    RNStepperArrowButtonCell *leftArrow = [[RNStepperArrowButtonCell alloc] initWithArrowDirection:YES];
    [leftArrow setParentCell:self];
    [self setLeftArrowCell:leftArrow];
    
    RNStepperArrowButtonCell *rightArrow = [[RNStepperArrowButtonCell alloc] initWithArrowDirection:NO];
    [rightArrow setParentCell:self];
    [self setRightArrowCell:rightArrow];
}

-(id)copyWithZone:(NSZone *)zone {
    RNStepperCell *cellCopy = [super copyWithZone:zone];
    cellCopy->leftArrowCell_ = nil;
    [cellCopy setLeftArrowCell:[[self leftArrowCell] copy]];
    [(RNStepperArrowButtonCell *)[cellCopy leftArrowCell] setParentCell:cellCopy];
    
    cellCopy->rightArrowCell_ = nil;
    [cellCopy setRightArrowCell:[[self rightArrowCell] copy]];
    [(RNStepperArrowButtonCell *)[cellCopy rightArrowCell] setParentCell:cellCopy];
    return cellCopy;
}

-(void)setFontSize:(CGFloat)pointSize {
    [self setArrowLength:(pointSize * 0.6)];
    [self setFont:[NSFont systemFontOfSize:pointSize]];
    [[self controlView] setNeedsDisplay:YES];
}

-(void)setArrowColor:(NSColor *)arrowColor {
    [(RNStepperArrowButtonCell *)[self leftArrowCell] setArrowColor:arrowColor];
    [(RNStepperArrowButtonCell *)[self rightArrowCell] setArrowColor:arrowColor];
}

-(void)setArrowPressedColor:(NSColor *)pressedArrowColor {
    [(RNStepperArrowButtonCell *)[self leftArrowCell] setPressedColor:pressedArrowColor];
    [(RNStepperArrowButtonCell *)[self rightArrowCell] setPressedColor:pressedArrowColor];
}


#pragma mark -
#pragma mark Event handling

- (BOOL)trackMouse:(NSEvent *)theEvent inRect:(NSRect)cellFrame ofView:(NSView *)controlView untilMouseUp:(BOOL)untilMouseUp {
    NSPoint eventPoint = [controlView convertPoint:[theEvent locationInWindow] fromView:nil];
    NSRect leftArrowFrame = [self leftArrowRectForBounds:cellFrame];
    NSRect rightArrowFrame = [self rightArrowRectForBounds:cellFrame];
    if ([controlView mouse:eventPoint inRect:leftArrowFrame]) {
        [[self leftArrowCell] setControlView:[self controlView]];
        BOOL returnValue = [[self leftArrowCell] trackMouse:theEvent inRect:leftArrowFrame ofView:controlView untilMouseUp:NO];
        if ([self sendsActionOnEndEditing] && [controlView isKindOfClass:[NSControl class]])
            [(NSControl *)controlView sendAction:[self action] to:[self target]];
        return returnValue;
    }
    if ([controlView mouse:eventPoint inRect:rightArrowFrame]) {
        [[self rightArrowCell] setControlView:[self controlView]];
        BOOL returnValue = [[self rightArrowCell] trackMouse:theEvent inRect:rightArrowFrame ofView:controlView untilMouseUp:untilMouseUp];
        if ([self sendsActionOnEndEditing] && [controlView isKindOfClass:[NSControl class]])
            [(NSControl *)controlView sendAction:[self action] to:[self target]];
        return returnValue;
    }
    return NO;
}

- (void)editWithFrame:(NSRect)aRect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)anObject event:(NSEvent *)theEvent {
    // Squeeze the height to exactly fit the text and make a little more width to accomodate the field editor (creeping into arrow cell territory)
    NSRect editFrame = NSInsetRect([self textAreaRectForBounds:aRect], -4.0f, ((NSHeight(aRect) - [[self attributedStringValue] size].height) / 2));
    [super editWithFrame:editFrame inView:controlView editor:textObj delegate:anObject event:theEvent];
}

- (void)selectWithFrame:(NSRect)aRect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)anObject start:(NSInteger)selStart length:(NSInteger)selLength {
    // Squeeze the height to exactly fit the text and make a little more width to accomodate the field editor (creeping into arrow cell territory)
    NSRect selectFrame = NSInsetRect([self textAreaRectForBounds:aRect], -4.0f, ((NSHeight(aRect) - [[self attributedStringValue] size].height) / 2));
    [super selectWithFrame:selectFrame inView:controlView editor:textObj delegate:anObject start:selStart length:selLength];
}


#pragma mark -
#pragma mark Drawing

- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
//    [self drawBezelWithFrame_:cellFrame inView:controlView];
    
    // Draw text vertically centered (not using textAreaRectForBounds: to ensure text is vertical centered)
    NSRect textFrame = NSInsetRect(cellFrame, 0.0f, ((NSHeight(cellFrame) - [[self attributedStringValue] size].height) / 2));
    NSInteger value = [self integerValue];
    if (value < 1 || value > 99)
        [self setIntegerValue:1];
    [super drawWithFrame:textFrame inView:controlView];
    
    [[self leftArrowCell] drawWithFrame:[self leftArrowRectForBounds:cellFrame] inView:controlView];
    [[self rightArrowCell] drawWithFrame:[self rightArrowRectForBounds:cellFrame] inView:controlView];
}

- (void)drawBezelWithFrame_:(NSRect)cellFrame inView:(NSView *)controlView {
    NSRect bezelFrame = cellFrame;
//    bezelFrame.size.height -= 1;
//    if (![controlView isFlipped])
//        bezelFrame.origin.y += 1;
    CGFloat radius = NSHeight(bezelFrame) / 2;
    NSBezierPath *borderPath = [NSBezierPath bezierPathWithRoundedRect:bezelFrame xRadius:radius yRadius:radius];
    
    // Background gradient
//    [stepperCellBackgroundGradient drawInBezierPath:borderPath angle:([controlView isFlipped] ? 90 : -90)];
    
    // Border
    [stepperCellBorderColor setStroke];
    [borderPath stroke];
    
    // Inner shadow
//    [stepperCellInnerShadow setShadowOffset:NSMakeSize(0.0f, ([controlView isFlipped] ? -1.0f : 1.0f))];
//    [borderPath fillWithInnerShadow:stepperCellInnerShadow];
}


#pragma mark -
#pragma mark Subelement frames

-(NSRect)leftArrowRectForBounds:(NSRect)bounds {
    NSRect arrowFrame = bounds;
    arrowFrame.size.width = 2 * [self arrowLength];
    return arrowFrame;
}

-(NSRect)rightArrowRectForBounds:(NSRect)bounds {
    NSRect arrowFrame = bounds;
    arrowFrame.size.width = 2 * [self arrowLength];
    arrowFrame.origin.x = NSMaxX(bounds) - 2 * [self arrowLength];
    return arrowFrame;
}

-(NSRect)textAreaRectForBounds:(NSRect)bounds {
    NSRect leftArrowBounds = [self leftArrowRectForBounds:bounds];
    NSRect rightArrowBounds = [self rightArrowRectForBounds:bounds];
    return NSMakeRect(NSMaxX(leftArrowBounds), NSMinY(bounds), NSMinX(rightArrowBounds) - NSMaxX(leftArrowBounds), NSHeight(bounds));
}

@end
